﻿// ———————————————————————–
// <copyright file="ContentObject.cs" company="The MITRE Corporation">
//    Copyright (c) 2010 The MITRE Corporation. All rights reserved.
//    Licensed under the Apache License, Version 2.0 (the "License");
//    you may not use this file except in compliance with the License.
//    You may obtain a copy of the License at
//    http://www.apache.org/licenses/LICENSE-2.0
//    Unless required by applicable law or agreed to in writing, software
//    distributed under the License is distributed on an "AS IS" BASIS,
//    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//    See the License for the specific language governing permissions and
//    limitations under the License.
// </copyright>
// ———————————————————————–
/////////////////////////////////////////////////////////////////////////////////////////////////
// ContentObject.cs:
// The contentObject is a container element for specific messages. The contentObject element MUST
// either contain an xmlContent content container or a nonXMLContent content container.
// Additional elements (metadata) used for specific distribution of the contentObject payload
// or hints for processing the payload are also present in the contentObject container element.
// Project: IC.NET - EDXL Library
//
// Language:    C#, .NET 4.0
// Platform:    Windows 7, VS 2010 Beta 2
// Author:      Don McGarry MITRE Rome

/////////////////////////////////////////////////////////////////////////////////////////////////

// Copyright (c) 2010 The MITRE Corporation. All rights reserved.
//
// NOTICE
// This software was produced for the U. S. Government
// under Contract No. FA8721-09-C-0001, and is
// subject to the Rights in Noncommercial Computer Software
// and Noncommercial Computer Software Documentation Clause
// (DFARS) 252.227-7014 (JUN 1995)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml;
using System.Xml.Linq;
using System.Xml.Serialization;

namespace EDXLSharp.EDXLDELib
{
  /// <summary>
  /// The contentObject is a container element for specific messages. The contentObject element MUST
  /// either contain an xmlContent content container or a nonXMLContent content container.
  /// Additional elements (metadata) used for specific distribution of the contentObject payload
  /// or hints for processing the payload are also present in the contentObject container element.
  /// </summary>
  [Serializable]
  public partial class ContentObject : IDEComposableObject
  {
    #region Private Member Variables

    /// <summary>
    /// The human-readable text describing the content object. 
    /// </summary>
    private string contentDescription;

    /// <summary>
    /// The topic related to the message data and content, as it may determine message distribution and presentation decisions. 
    /// </summary>
    private VLList contentKeyword;

    /// <summary>
    /// The human-readable text uniquely identifying the incident/event/situation associated with the contentObject. 
    /// </summary>
    private string incidentID;

    /// <summary>
    /// The human-readable text describing the incident/event/situation associated with the contentObject. 
    /// </summary>
    private string incidentDescription;

    /// <summary>
    /// The functional role of the message originator, as it may determine message distribution and presentation decisions. 
    /// </summary>
    private VLList originatorRole;

    /// <summary>
    /// The functional role of the message consumer, as it may determine message distribution and presentation decisions. 
    /// </summary>
    private VLList consumerRole;

    /// <summary>
    /// Special requirements regarding confidentiality of the content of this contentObject. 
    /// </summary>
    private string confidentiality;

    /// <summary>
    /// Container for content provided in a non-XML MIME type. 
    /// </summary>
    private NonXMLContentType nonXMLContent;

    /// <summary>
    /// Container for valid-namespaced XML data. 
    /// </summary>
    private XMLContentType xmlContent;

    /// <summary>
    /// Special requirements allowing for signature of the content of a contentObject. 
    /// </summary>
    private List<XElement> other;

    #endregion

    #region Constructors
    /// <summary>
    /// Initializes a new instance of the ContentObject class
    /// Default Ctor - Initializes Lists
    /// </summary>
    public ContentObject()
    {
      this.contentKeyword = new VLList("contentKeyword");
      this.consumerRole = new VLList("consumerRole");
      this.originatorRole = new VLList("originatorRole");
      this.other = new List<XElement>();
    }

    /// <summary>
    /// Initializes a new instance of the ContentObject class
    /// Ctor To Initialize The Content Keyword Value List
    /// </summary>
    /// <param name="contentkeywordURN">Name of the ContentKeyword List</param>
    /// <param name="contentkeywordsin">List of Keywords</param>
    public ContentObject(string contentkeywordURN, List<string> contentkeywordsin)
    {
      this.contentKeyword = new VLList("contentKeyword");
      this.contentKeyword.Add(new ValueList(contentkeywordURN, contentkeywordsin));
      this.consumerRole = new VLList("consumerRole");
      this.originatorRole = new VLList("originatorRole");
      this.other = new List<XElement>();
    }
    #endregion

    #region Public Accessors

    /// <summary>
    /// Gets or sets
    /// Description of the Content Contained in this CO
    /// </summary>
    [XmlElement(ElementName = "contentDescription", Order = 0)]
    public string ContentDescription
    {
      get { return this.contentDescription; }
      set { this.contentDescription = value; }
    }

    /// <summary>
    /// Gets or sets
    /// The list and associated val(s) is in the form:
    /// where the content is the Name of a published list of
    /// values and definitions, and the content of val is a string denoting the val itself.
    /// </summary>
    [XmlIgnore]
    public List<ValueList> ContentKeyword
    {
      get { return this.contentKeyword; }
      set { this.contentKeyword.Values = value; }
    }

    /// <summary>
    /// Gets or sets
    /// UID For This Incident
    /// </summary>
    [XmlElement(ElementName = "incidentID", Order = 2)]
    public string IncidentID
    {
      get { return this.incidentID; }
      set { this.incidentID = value; }
    }

    /// <summary>
    /// Gets or sets
    /// Free Text Area Describing the Incident
    /// </summary>
    [XmlElement(ElementName = "incidentDescription", Order = 3)]
    public string IncidentDescription
    {
      get { return this.incidentDescription; }
      set { this.incidentDescription = value; }
    }

    /// <summary>
    /// Gets or sets
    /// The list and associated val(s) is in the form:
    /// where the content is the Name of a published list of
    /// values and definitions, and the content of val is a string denoting the val itself.
    /// </summary>
    [XmlIgnore]
    public List<ValueList> OriginatorRole
    {
      get { return this.originatorRole; }
      set { this.originatorRole.Values = value; }
    }

    /// <summary>
    /// Gets or sets
    /// The list and associated val(s) is in the form:
    /// where the content is the Name of a published list of
    /// values and definitions, and the content of val is a string denoting the val itself.
    /// </summary>
    [XmlIgnore]
    public List<ValueList> ConsumerRole
    {
      get { return this.consumerRole; }
      set { this.consumerRole.Values = value; }
    }

    /// <summary>
    /// Gets or sets
    /// Confidentiality Descriptor of the Content
    /// </summary>
    [XmlElement(ElementName = "confidentiality", Order = 6)]
    public string Confidentiality
    {
      get { return this.confidentiality; }
      set { this.confidentiality = value; }
    }

    /// <summary>
    /// Gets or sets
    /// Payload of MIME Content
    /// </summary>
    [XmlElement(ElementName = "nonXMLContent", Order = 7)]
    public NonXMLContentType NonXMLContent
    {
      get { return this.nonXMLContent; }
      set { this.nonXMLContent = value; }
    }

    /// <summary>
    /// Gets or sets
    /// Payload of XML Content
    /// </summary>
    [XmlElement(ElementName = "xmlContent", Order = 8)]
    public XMLContentType XMLContent
    {
      get { return this.xmlContent; }
      set { this.xmlContent = value; }
    }

    /// <summary>
    /// Gets or sets
    /// Any:Other for Custom Data or Key/Signature Info
    /// </summary>
    [XmlIgnore]
    public List<XElement> Other
    {
      get { return this.other; }
      set { this.other = value; }
    }

    /// <summary>
    /// Gets or sets
    /// The list and associated val(s) is in the form:
    /// where the content is the Name of a published list of
    /// values and definitions, and the content of val is a string denoting the val itself.
    /// </summary>
    [XmlElement(ElementName = "contentKeyword", Order = 1)]
    public ValueList[] ContentKeywordXML
    {
      get
      {
        if (this.contentKeyword.Count == 0)
        {
          return null;
        }
        else
        {
          return this.contentKeyword.ValuesXML;
        }
      }

      set
      {
        if (value != null)
        {
          this.contentKeyword.ValuesXML = value;
        }
      }
    }

    /// <summary>
    /// Gets or sets
    /// XML Serialization Object for Originator Role
    /// </summary>
    [XmlElement(ElementName = "originatorRole", Order = 4)]
    public ValueList[] OriginatorRoleXML
    {
      get
      {
        if (this.originatorRole.Count == 0)
        {
          return null;
        }
        else
        {
          return this.originatorRole.ValuesXML;
        }
      }

      set
      {
        if (value != null)
        {
          this.originatorRole.ValuesXML = value;
        }
      }
    }

    /// <summary>
    /// Gets or sets
    /// XML Serialization Object for Consumer Role
    /// </summary>
    [XmlElement(ElementName = "consumerRole", Order = 5)]
    public ValueList[] ConsumerRoleXML
    {
      get
      {
        if (this.consumerRole.Count == 0)
        {
          return null;
        }
        else
        {
          return this.consumerRole.ValuesXML;
        }
      }

      set
      {
        if (value != null)
        {
          this.consumerRole.ValuesXML = value;
        }
      }
    }

    /// <summary>
    /// Gets or sets
    /// XML Serialization Object for Xml Any Other
    /// </summary>
    [XmlAnyElement(Order = 9)]
    public XElement[] OtherXML
    {
      get
      {
        if (this.other.Count == 0)
        {
          return null;
        }
        else
        {
          return this.other.ToArray();
        }
      }

      set
      {
        if (value != null)
        {
          this.other = value.ToList();
        }
      }
    }

    #endregion

    #region Public Member Functions

    /// <summary>
    /// Adds Any Other XML Content To The List
    /// </summary>
    /// <param name="otherxmlin">Valid XML Element</param>
    public void AddOther(XElement otherxmlin)
    {
      this.other.Add(otherxmlin);
    }

    /// <summary>
    /// Parses The Keyword Value Lists Looking for a Specfic Named List 
    /// </summary>
    /// <param name="valuelisturnin">Unique Name of the List To Search For</param>
    /// <returns>Index of the Named List</returns>
    public int IndexOfNamedKeywordList(string valuelisturnin)
    {
      int index = this.contentKeyword.FindIndex(delegate(ValueList vl) { return vl.ValueListURN == valuelisturnin; });
      return index;
    }

    /// <summary>
    /// Writes This IComosable Message Into An Existing XML Document
    /// </summary>
    /// <param name="xwriter">Existing XML Document Writer</param>
    internal override void WriteXML(XmlWriter xwriter)
    {
      this.Validate();
      if (!String.IsNullOrEmpty(this.contentDescription))
      {
        xwriter.WriteElementString("contentDescription", this.contentDescription);
      }

      this.contentKeyword.WriteXML(xwriter);
      if (!String.IsNullOrEmpty(this.incidentID))
      {
        xwriter.WriteElementString("incidentID", this.incidentID);
      }

      if (!String.IsNullOrEmpty(this.incidentDescription))
      {
        xwriter.WriteElementString("incidentDescription", this.incidentDescription);
      }

      this.originatorRole.WriteXML(xwriter);
      this.consumerRole.WriteXML(xwriter);
      if (!String.IsNullOrEmpty(this.confidentiality))
      {
        xwriter.WriteElementString("confidentiality", this.confidentiality);
      }

      if (this.nonXMLContent != null)
      {
        xwriter.WriteStartElement("nonXMLContent");
        this.nonXMLContent.WriteXML(xwriter);
        xwriter.WriteEndElement();
      }

      if (this.xmlContent != null)
      {
        xwriter.WriteStartElement("xmlContent");
        this.xmlContent.WriteXML(xwriter);
        xwriter.WriteEndElement();
      }

      if (this.other.Count != 0)
      {
        foreach (XElement xe in this.other)
        {
          xe.WriteTo(xwriter);
        }
      }
    }

    /// <summary>
    /// Reads This IComposable Message From An Existing XML Document
    /// </summary>
    /// <param name="rootnode">Node That Points to the TargetArea Element</param>
    internal override void ReadXML(XmlNode rootnode)
    {
      this.contentKeyword.ReadXML(rootnode);
      this.consumerRole.ReadXML(rootnode);
      this.originatorRole.ReadXML(rootnode);

      foreach (XmlNode node in rootnode.ChildNodes)
      {
        ////This check is for xml objects that have attribs only (no innertext) like CoT
        if (node.LocalName == "xmlContent" && String.IsNullOrEmpty(node.InnerXml))
        {
          continue;
        }
        else if (node.LocalName != "xmlContent" && String.IsNullOrEmpty(node.InnerText))
        {
          continue;
        }

        switch (node.LocalName)
        {
          case "contentDescription":
            this.contentDescription = node.InnerText;
            break;
          case "contentKeyword":
            break;
          case "incidentID":
            this.incidentID = node.InnerText;
            break;
          case "incidentDescription":
            this.incidentDescription = node.InnerText;
            break;
          case "originatorRole":
            break;
          case "consumerRole":
            break;
          case "confidentiality":
            this.confidentiality = node.InnerText;
            break;
          case "nonXMLContent":
            this.nonXMLContent = new NonXMLContentType();
            this.nonXMLContent.ReadXML(node);
            break;
          case "xmlContent":
            this.xmlContent = new XMLContentType();
            this.xmlContent.ReadXML(node);
            break;
          case "#comment":
            break;
          default:
            XElement xe = XElement.Load(new XmlNodeReader(node));
            this.other.Add(xe);
            break;
        }
      }

      this.Validate();
    }

    #endregion

    #region Private Member Functions

    /// <summary>
    /// Validates This Message Element For Required Values and Conformance
    /// </summary>
    protected override void Validate()
    {
      if (this.xmlContent == null && this.nonXMLContent == null)
      {
        throw new ArgumentNullException("This Content Object Has No Content");
      }

      if (this.xmlContent != null && this.nonXMLContent != null)
      {
        throw new ArgumentException("Content Objects Can't Carry Both XML and Non-XML Content");
      }
    }

    #endregion
  }
}
